package com.netty.mvc;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.netty.common.Result;

import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.stream.Collectors;

public class MVC {

    /**
     * 方法与url的key-value
     */
    private HashMap<String, Method> methodMap = new HashMap<String, Method>();

    ObjectMapper objectMapper = new ObjectMapper();

    /**
     * 包扫描下的类，将其实例化存储到map中, 模拟ioc容器
     * 与url存储起来
     */
    private HashMap<String, Object> iocMap = new HashMap<String, Object>();

    /**
     * 包扫描下的类，将其实例化存储到map中, 模拟ioc容器
     * 以类的名称作key
     */
    private HashMap<String, Object> nameIocMap = new HashMap<String, Object>();


    public MVC() {
        scanner("com.netty.controller");
        Set<Map.Entry<String, Object>> entries = nameIocMap.entrySet();
        entries.stream().forEach(entry->{
            Object value = entry.getValue();
            Field[] fields = value.getClass().getDeclaredFields();
            Arrays.stream(fields).forEach(field -> {
                MyAutowired annotation = field.getAnnotation(MyAutowired.class);
                if(annotation!=null){
                    String name = field.getType().getName();
                    field.setAccessible(true);
                    Object fieldObject = nameIocMap.get(name);
                    try{
                        field.set(value,fieldObject);
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }
            });
        });
    }


    public Object deal(final String url, String methodType, Map<String, Object> parameterMap) throws Exception {
        Method method = null;
        Object invokeClass = null;
        List<Map.Entry<String, Method>> collect = methodMap.entrySet().stream().filter(stringMethodEntry -> {
            return url.contains(stringMethodEntry.getKey());
        }).collect(Collectors.toList());
        if (collect.size() > 0) {
            method = collect.get(0).getValue();
        }

        List<Map.Entry<String, Object>> collect2 = iocMap.entrySet().stream().filter(stringMethodEntry -> {
            return url.contains(stringMethodEntry.getKey());
        }).collect(Collectors.toList());
        if (collect2.size() > 0) {
            invokeClass = collect2.get(0).getValue();
            String name = invokeClass.getClass().getName();
            Object o = nameIocMap.get(name);
            invokeClass=o;
        }
        try {
            if (method == null) {
                //没有当前链接，则为404
                throw new RuntimeException("404 not found");
            }

            //判断请求类型
            MyRequestMapping myRequestMapping = method.getAnnotation(MyRequestMapping.class);
            MyRequestMethod methodRequestMethod = myRequestMapping.method();
            String methodName = methodRequestMethod.name().toLowerCase();

            if (!methodName.equals(methodType)) {
                //没有当前链接，则为404
                throw new RuntimeException("405 请求类型有误");
            }


            //方法的参数类型
            Parameter[] parameters = method.getParameters();
            final List<Parameter> classes = Arrays.asList(parameters);
            final Object[] param = new Object[parameters.length];

            //请求参数列表
            Set<Map.Entry<String, Object>> entries = parameterMap.entrySet();

            Arrays.stream(parameters).forEach(parameter -> {
                int i = classes.indexOf(parameter);
                MyRequestParam requestParam = parameter.getAnnotation(MyRequestParam.class);
                MyRequestBody requestBody = parameter.getAnnotation(MyRequestBody.class);
                if (requestBody != null) {
                    //json请求
                    String json = parameterMap.get("json").toString();
                    Class<?> type = parameter.getType();
                    try {
                        Object o = objectMapper.readValue(json, type);
                        param[i] = o;
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                } else {
                    String fieldName = requestParam.value();
                    Class<?> type = parameter.getType();
                    Map.Entry<String, Object> stringObjectEntry = null;
                    try {
                        stringObjectEntry = entries.stream().filter(entry -> {
                            return fieldName.equals(entry.getKey());
                        }).findFirst().get();
                    } catch (Exception e) {
                        //找不到相关属性
                    }
                    if (stringObjectEntry != null) {
                        Object value = stringObjectEntry.getValue();
                        String name = type.getSimpleName().toLowerCase();
                        switch (name){
                            case "long":
                                value=Long.valueOf(value.toString());
                                break;
                            case "int":
                            case "integer":
                                value=Integer.valueOf(value.toString());
                                break;
                            case "float":
                                value=Float.valueOf(value.toString());
                                break;
                            case "double":
                                value=Double.valueOf(value.toString());
                                break;
                        }
                        param[i] = value;
                    }
                }
            });
            Object invoke = method.invoke(invokeClass, param);
            Result result = (Result) invoke;
            Object value = result.getValue();
            if (result.isSuccess()) {
                if (value instanceof HashMap) {
                    if (((HashMap) value).containsKey("file")) {
                        return value;
                    }
                }
            }
            ObjectMapper objectMapper = new ObjectMapper();
            String s = objectMapper.writeValueAsString(invoke);
            return s;
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        }
    }


    private void scanner(String scanPackage) {
        String s = scanPackage.replaceAll("\\.", "/");
        String path = "";
        try {
            //开发环境非jar包
            path = this.getClass().getResource("/").getPath();
            path = path + "\\" + s;
            File file = new File(path);
            File[] files = file.listFiles();
            Arrays.stream(files).forEach(fileOne -> {
                try {
                    if (fileOne.isDirectory()) {
                        scanner(scanPackage + "." + fileOne.getName());
                    } else {
                        instance(fileOne, scanPackage);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        } catch (Exception e) {
            //线上环境打jar包
            try {
                String jarPath = this.getClass().getProtectionDomain().getCodeSource().getLocation().getPath();
                JarFile localJarFile = new JarFile(new File(jarPath));
                Enumeration<JarEntry> entries = localJarFile.entries();
                while (entries.hasMoreElements()) {
                    JarEntry jarEntry = entries.nextElement();
                    String innerPath = jarEntry.getName();
                    if (innerPath.startsWith(s) && innerPath.endsWith("class")) {
                        instanceByClass(innerPath);
                    }
                }
            } catch (Exception jare) {

            }
        }

    }

    private void instanceByClass(String className) throws Exception {
        className = className.replaceAll(".class", "");
        className = className.replaceAll("/", ".");
        final Class aClass = Class.forName(className);
        dealInstance(aClass);
    }

    private void instance(File fileOne, String scanPackage) throws Exception {
        if (fileOne.getName().contains(".class")) {
            String className = fileOne.getName().replaceAll(".class", "");
            final Class aClass = Class.forName(scanPackage + "." + className);
            dealInstance(aClass);
        }
    }

    private void dealInstance(Class aClass) throws Exception {
        MyController myController = (MyController) aClass.getAnnotation(MyController.class);
        MyRequestMapping classRequestMapping = (MyRequestMapping) aClass.getAnnotation(MyRequestMapping.class);
        if (myController != null) {
            StringBuffer classUrlBuffer = new StringBuffer();
            if (classRequestMapping != null) {
                String s = classRequestMapping.value() == null ? "" : classRequestMapping.value();
                if (!s.startsWith("/") && !s.startsWith("\\")) {
                    s = "/" + s;
                }
                classUrlBuffer.append(s);
            }
            final String classUrl = classUrlBuffer.toString();
            Object targetObject = aClass.newInstance();
            nameIocMap.put(aClass.getName(),targetObject);
            Method[] methods = aClass.getMethods();
            Arrays.stream(methods).forEach(method -> {
                toMap(method, classUrl, targetObject);
            });
        }
    }


    private void toMap(Method method, String classUrl, Object targetObject) {
        try {
            MyRequestMapping myRequestMapping = method.getAnnotation(MyRequestMapping.class);
            if (myRequestMapping != null) {
                String methodUrl = myRequestMapping.value();
                if (!methodUrl.startsWith("/") && !methodUrl.startsWith("\\")) {
                    methodUrl = "/" + methodUrl;
                }
                String resultUrl = classUrl + methodUrl;
                methodMap.put(resultUrl, method);
                iocMap.put(resultUrl, targetObject);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
